---
title: Adding pages
description: How to add new pages to your app
---

In this guide you'll learn how to add new pages to your application and add some
basic data fetching.

For the sake of this guide, let's add a new projects features with a list page
and a details page with some tabs to illustrate how you can use layouts and data
prefetching.

## Project list page

Let's start by creating the list page. Create a new file in
`apps/web/features/projects/list/list-page.tsx`

We group all project list page related files in a `list` folder to keep things
organized.

```tsx
'use client'

export function ProjectsListPage(props: {
  params: {
    workspace: string
  }
}) {
  return <div>Projects list</div>
}
```

Note that `'use client'` directive is added to the file. This is necessary
because we're going to use React state and hooks. We typically use the
convention of using server component for the route file to prefetch data for the
interactive parts of the page.

We also already added a `params` type to the props, since this feature is part
of a workspace it will accept a `workspace` parameter in the URL.

### List page route

The route for this page will be `(app)/[workspace]/(dashboard)/projects`, let's
go ahead and create the route file in
`apps/web/app/(app)/[workspace]/(dashboard)/projects/page.tsx`.

```tsx
import { ProjectsListPage } from '#features/projects/list/list-page'
import { createPage } from '#lib/create-page'

const { Page, metadata } = createPage({
  title: 'Projects',
  component: ProjectsListPage,
})

export { metadata }

export default Page
```

The `createPage` function is a helper that allows you to validate route
parameters and search params, and also prefetch data for the page.

### List page navigation

Now that the page and route are created, we can add a navigation link to the
sidebar.

Open `apps/web/features/common/components/sidebar.tsx` and add a new nav item to
the navigation.

```tsx
<NavGroup>
  // ... other links
  <AppSidebarLink
    href={usePath('contacts')}
    isActive={useActivePath('contacts', { end: false })}
    label="Contacts"
    icon={<FiUsers />}
    hotkey="navigation.contacts"
  />
  <AppSidebarLink
    href={usePath('projects')}
    isActive={useActivePath('projects', { end: false })}
    label="Projects"
    icon={<FiFolder />}
    hotkey="navigation.projects"
  />
</NavGroup>
```

The `usePath` hook returns the path including the current workspace slug.

The `useActivePath` hook returns `true` if the current path matches the given
path. The `end: false` will make sure that all `/projects/*` paths will be
matched.

Now open the application and navigate to the new projects page.

### List page fetching data

Let's assume we already have a `projects` API endpoint that returns a list of
projects.

Open the `list-page.tsx` file and add a `useQuery` hook to fetch the projects.

```tsx
'use client'

import { DataGrid, Page, PageBody, PageHeader } from '@saas-ui-pro/react'
import { LoadingOverlay, LoadingSpinner } from '@saas-ui/react'
import { useQuery } from '@tanstack/react-query'

import { ProjectDTO } from '@acme/api/types'

import { useCurrentWorkspace } from '#features/common/hooks/use-current-workspace'
import { useTRPC } from '#lib/trpc/react'

export function ProjectsListPage(props: {
  params: {
    workspace: string
  }
}) {
  const [workspace] = useCurrentWorkspace()

  const trpc = useTRPC()

  const { data, isLoading } = useQuery(
    trpc.projects.list.queryOptions({
      workspaceId: workspace.id,
    }),
  )

  const columns = useColumns<ProjectDTO>(
    (helper) => [
      helper.accessor('name', {
        header: 'Name',
      }),
    ],
    [],
  )

  return (
    <Page>
      <PageHeader title="Projects" />
      <PageBody contentWidth="full">
        {isLoading ? (
          <LoadingOverlay>
            <LoadingSpinner />
          </LoadingOverlay>
        ) : (
          <DataGrid data={data ?? []} columns={columns} />
        )}
      </PageBody>
    </Page>
  )
}
```

Voila! You've just created your first page with data fetching.
